package top.javarem.medical.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import top.javarem.medical.domain.dto.AnswerResultDTO;
import top.javarem.medical.domain.dto.AnswerSubmitDTO;
import top.javarem.medical.domain.dto.MedicalKnowledgeDTO;
import top.javarem.medical.domain.dto.QuestionDTO;
import top.javarem.medical.domain.po.Knowledge;
import top.javarem.medical.domain.po.Question;
import top.javarem.medical.domain.po.QuestionSubmitOrder;
import top.javarem.medical.domain.vo.AdminQuestionVO;
import top.javarem.medical.domain.vo.QuestionDetailVO;
import top.javarem.medical.domain.vo.QuestionVO;
import top.javarem.medical.mapper.QuestionMapper;
import top.javarem.medical.service.QuestionService;
import top.javarem.medical.service.QuestionSubmitOrderService;
import top.javarem.medical.service.UserCreditService;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @description 针对表【question(题库表)】的数据库操作Service实现
 */
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question>
        implements QuestionService {

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private QuestionSubmitOrderService questionSubmitOrderService;
    @Resource
    private UserCreditService userCreditService;

    @Override
    public List<QuestionDTO> queryByCategoryId(Integer categoryId) {

        //        先从缓存获取
        String cacheKey = "Medical_Questions_Key_" + categoryId;
        RBucket<List<QuestionDTO>> bucket = redissonClient.<List<QuestionDTO>>getBucket(cacheKey);
        List<QuestionDTO> questionDTOList = bucket.get();
        if (!CollectionUtils.isEmpty(questionDTOList)) {
            Collections.shuffle(questionDTOList);
            return questionDTOList.subList(0, 10);
        }
//        再从数据库查询
        List<Question> questionList = lambdaQuery()
                .eq(Question::getCategoryId, categoryId)
                .eq(Question::getStatus, 1)
                .list();
        questionDTOList = new ArrayList<>();
        for (Question question : questionList) {
//            分割并封装选项
            List<String> splitOptions = splitOptions(question.getOptions());

            questionDTOList.add(QuestionDTO.builder()
                    .id(question.getId())
                    .categoryId(question.getCategoryId())
                    .content(question.getContent())
                    .options(splitOptions)
                    .difficulty(question.getDifficulty())
                    .build());
        }
        Collections.shuffle(questionDTOList);
        bucket.set(questionDTOList, 3, TimeUnit.HOURS);
        return questionDTOList.subList(0, 10);

    }

    @Override
    @Transactional
    public AnswerResultDTO checkAnswer(AnswerSubmitDTO answerSubmitDTO) {
        String cacheKey = "Answer_Map_Key_" + answerSubmitDTO.getCategoryId();
        RMap<Long, Question> answerMap = redissonClient.<Long, Question>getMap(cacheKey);
        if (CollectionUtils.isEmpty(answerMap)) {
            // 3. 批量查询题目信息
            List<Question> questionList = lambdaQuery()
                    .select(Question::getId, Question::getAnswer, Question::getDifficulty, Question::getExplanation)
                    .eq(Question::getCategoryId, answerSubmitDTO.getCategoryId())
                    .list();
            Map<Long, Question> map = questionList.stream()
                    .collect(Collectors.toMap(Question::getId, x -> x));
            answerMap.putAll(map);
            answerMap.expire(3, TimeUnit.HOURS);
        }
        // 4. 计算得分和正确数
        int totalScore = 0;
        int correctCount = 0;

        for (AnswerSubmitDTO.QuestionAnswer answer : answerSubmitDTO.getAnswers()) {
            Question question = answerMap.get(answer.getId());
            boolean isCorrect = question.getAnswer().equals(answer.getUserAnswer());

            if (isCorrect) {
                // 计算该题得分
                totalScore += calculateScore(question.getDifficulty());
                correctCount++;
            }

        }

        // 5. 设置结果 提交问答订单
        QuestionSubmitOrder questionSubmitOrder = QuestionSubmitOrder.builder()
                .submitId(RandomStringUtils.randomNumeric(11))
                .totalscore(totalScore)
                .correctCount(correctCount)
                .createAt(new Date())
                .build();
        boolean save = questionSubmitOrderService.save(questionSubmitOrder);
        if (!save) {
            throw new RuntimeException("提交问答订单失败");
        }

//        更新用户积分
        boolean update = userCreditService.updateUserCredit(totalScore);
        if (!update) {
            throw new RuntimeException("用户积分更新失败！");
        }

        return AnswerResultDTO.builder()
                .correctCount(correctCount)
                .totalScore(totalScore)
                .build();
    }

    /**
     * 根据题目难度计算分数
     */
    private int calculateScore(Integer difficulty) {
        switch (difficulty) {
            case 1:
                return 1;
            case 2:
                return 2;
            case 3:
                return 4;
            case 4:
                return 6;
            case 5:
                return 8;
            default:
                return 0;
        }
    }

    private List<String> splitOptions(String options) {
        String[] split = options.split(",");
        if (split.length != 4) {
            throw new RuntimeException("数据库问题选择配置错误");
        }
        List<String> splitOptions = new ArrayList<>(split.length);
        for (int i = 0; i < split.length; i++) {
            splitOptions.add(split[i]);
        }
        return splitOptions;
    }

    @Override
    public List<AdminQuestionVO> pageQuery(Integer categoryId, String type, Integer difficulty,
                                           Integer pageNum, Integer pageSize) {
        List<Question> questionList = lambdaQuery()
                .eq(categoryId != null, Question::getCategoryId, categoryId)
                .eq(StringUtils.isNotBlank(type), Question::getType, type)
                .eq(difficulty != null, Question::getDifficulty, difficulty)
                .eq(Question::getStatus, 1)
                .orderByDesc(Question::getCreatedAt)
                .last("limit " + (pageNum - 1) * pageSize + pageSize)
                .list();
        List<AdminQuestionVO> questionVOList = new ArrayList<>();
        for (Question question : questionList) {
            AdminQuestionVO questionVO = new AdminQuestionVO();
            BeanUtils.copyProperties(question, questionVO);
            questionVOList.add(questionVO);
        }
        return questionVOList;
    }

    @Override
    public Boolean addQuestion(QuestionVO vo) {
        Question question = new Question();
        BeanUtils.copyProperties(vo, question);
        // 将选项List转为字符串存储
        question.setOptions(vo.getOptions());
        question.setStatus(1);
        return save(question);
    }

    @Override
    public Boolean updateQuestion(QuestionVO vo) {
        Question question = new Question();
        BeanUtils.copyProperties(vo, question);
        return updateById(question);
    }

    @Override
    public Boolean deleteQuestion(Long id) {
        return removeById(id);
    }

    @Override
    public QuestionDetailVO queryById(Integer id) {

        Question one = lambdaQuery().eq(Question::getId, id).one();
        QuestionDetailVO questionDTO = new QuestionDetailVO();
        BeanUtils.copyProperties(one, questionDTO);
        return questionDTO;

    }

    private QuestionVO convertToVO(Question question) {
        QuestionVO vo = new QuestionVO();
        BeanUtils.copyProperties(question, vo);
        vo.setOptionList(Arrays.asList(question.getOptions().split(",")));
        return vo;
    }
}
